<!DOCTYPE html>
<html>
<head>
    

    

    



    <meta charset="utf-8">
    
    
    
    
    <title>【置顶】并发——抽象队列同步器AQS的实现原理 | 博客主页 | 世界是个球，前方总有路！</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="theme-color" content="#3F51B5">
    
    
    <meta name="keywords" content="并发">
    <meta name="description" content="一、前言&amp;emsp;&amp;emsp;这段时间在研究Java并发相关的内容，一段时间下来算是小有收获了。ReentrantLock是Java并发中的重要部分，所以也是我的首要研究对象，在学习它的过程中，我发现它是基于抽象队列同步器（AQS）实现的，所以我花了点时间学习了一下AQS的实现原理。这篇博客就来说一说AQS的作用，以及它是如何实现的。   二、正文2.1 什么是AQS&amp;emsp;&amp;emsp;AQ">
<meta property="og:type" content="article">
<meta property="og:title" content="【置顶】并发——抽象队列同步器AQS的实现原理">
<meta property="og:url" content="http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/index.html">
<meta property="og:site_name" content="博客主页">
<meta property="og:description" content="一、前言&amp;emsp;&amp;emsp;这段时间在研究Java并发相关的内容，一段时间下来算是小有收获了。ReentrantLock是Java并发中的重要部分，所以也是我的首要研究对象，在学习它的过程中，我发现它是基于抽象队列同步器（AQS）实现的，所以我花了点时间学习了一下AQS的实现原理。这篇博客就来说一说AQS的作用，以及它是如何实现的。   二、正文2.1 什么是AQS&amp;emsp;&amp;emsp;AQ">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/1.png">
<meta property="og:image" content="http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/2.png">
<meta property="og:image" content="http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/3.png">
<meta property="og:image" content="http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/4.png">
<meta property="article:published_time" content="2022-04-09T06:35:20.000Z">
<meta property="article:modified_time" content="2020-04-09T14:17:08.784Z">
<meta property="article:author" content="特务依昂">
<meta property="article:tag" content="并发">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/1.png">
    
        <link rel="alternate" type="application/atom+xml" title="博客主页" href="/blog/atom.xml">
    
    <link rel="shortcut icon" href="/blog/img/title.png">
    <link rel="stylesheet" href="//unpkg.com/hexo-theme-material-indigo@latest/css/style.css">
    <script>window.lazyScripts=[]</script>

    <!-- custom head -->
    

<meta name="generator" content="Hexo 4.2.0"></head>

<body>
    <div id="loading" class="active"></div>

    <aside id="menu" class="hide" >
  <div class="inner flex-row-vertical">
    <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menu-off">
        <i class="icon icon-lg icon-close"></i>
    </a>
    <div class="brand-wrap" style="background-image:url(/blog/img/brand.jpg)">
      <div class="brand">
        <a href="/blog/" class="avatar waves-effect waves-circle waves-light">
          <img src="/blog/img/avatar.jpg">
        </a>
        <hgroup class="introduce">
          <h5 class="nickname">特务依昂</h5>
          <a href="mailto:1131564805@qq.com" title="1131564805@qq.com" class="mail">1131564805@qq.com</a>
        </hgroup>
      </div>
    </div>
    <div class="scroll-wrap flex-col">
      <ul class="nav">
        
            <li class="waves-block waves-effect">
              <a href="/blog/"  >
                <i class="icon icon-lg icon-home"></i>
                主页
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/archives"  >
                <i class="icon icon-lg icon-archives"></i>
                博客
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/tags"  >
                <i class="icon icon-lg icon-tags"></i>
                标签
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/categories"  >
                <i class="icon icon-lg icon-th-list"></i>
                分类
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://github.com/tewuyiang" target="_blank" >
                <i class="icon icon-lg icon-github"></i>
                Github
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://weibo.com/u/5516635708/" target="_blank" >
                <i class="icon icon-lg icon-weibo"></i>
                Weibo
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://www.cnblogs.com/tuyang1129/" target="_blank" >
                <i class="icon icon-lg icon-link"></i>
                博客园
              </a>
            </li>
        
      </ul>
    </div>
  </div>
</aside>

    <main id="main">
        <header class="top-header" id="header">
    <div class="flex-row">
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light on" id="menu-toggle">
          <i class="icon icon-lg icon-navicon"></i>
        </a>
        <div class="flex-col header-title ellipsis">【置顶】并发——抽象队列同步器AQS的实现原理</div>
        
        <div class="search-wrap" id="search-wrap">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
                <i class="icon icon-lg icon-chevron-left"></i>
            </a>
            <input type="text" id="key" class="search-input" autocomplete="off" placeholder="输入感兴趣的关键字">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="search">
                <i class="icon icon-lg icon-search"></i>
            </a>
        </div>
        
        
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menuShare">
            <i class="icon icon-lg icon-share-alt"></i>
        </a>
        
    </div>
</header>
<header class="content-header post-header">

    <div class="container fade-scale">
        <h1 class="title">【置顶】并发——抽象队列同步器AQS的实现原理</h1>
        <h5 class="subtitle">
            
                <time datetime="2022-04-09T06:35:20.000Z" itemprop="datePublished" class="page-time">
  2022-04-09
</time>


	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/blog/categories/%E5%B9%B6%E5%8F%91/">并发</a></li></ul>

            
        </h5>
    </div>

    


</header>


<div class="container body-wrap">
    
    <aside class="post-widget">
        <nav class="post-toc-wrap post-toc-shrink" id="post-toc">
            <h4>TOC</h4>
            <ol class="post-toc"><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#一、前言"><span class="post-toc-number">1.</span> <span class="post-toc-text">一、前言</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#二、正文"><span class="post-toc-number">2.</span> <span class="post-toc-text">二、正文</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-1-什么是AQS"><span class="post-toc-number">2.1.</span> <span class="post-toc-text">2.1 什么是AQS</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-2-如何使用AQS"><span class="post-toc-number">2.2.</span> <span class="post-toc-text">2.2 如何使用AQS</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-3-AQS中的方法"><span class="post-toc-number">2.3.</span> <span class="post-toc-text">2.3 AQS中的方法</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-4-AQS如何实现线程同步"><span class="post-toc-number">2.4.</span> <span class="post-toc-text">2.4 AQS如何实现线程同步</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-5-独占锁的获取与释放过程"><span class="post-toc-number">2.5.</span> <span class="post-toc-text">2.5 独占锁的获取与释放过程</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-6-共享锁的获取与释放过程"><span class="post-toc-number">2.6.</span> <span class="post-toc-text">2.6 共享锁的获取与释放过程</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-7-使用AQS实现一个锁"><span class="post-toc-number">2.7.</span> <span class="post-toc-text">2.7 使用AQS实现一个锁</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#2-8-AQS如何实现线程等待"><span class="post-toc-number">2.8.</span> <span class="post-toc-text">2.8 AQS如何实现线程等待</span></a></li></ol></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#三、总结"><span class="post-toc-number">3.</span> <span class="post-toc-text">三、总结</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#四、参考"><span class="post-toc-number">4.</span> <span class="post-toc-text">四、参考</span></a></li></ol>
        </nav>
    </aside>


<article id="post-并发——抽象队列同步器AQS的实现原理"
  class="post-article article-type-post fade" itemprop="blogPost">

    <div class="post-card">
        <h1 class="post-card-title">【置顶】并发——抽象队列同步器AQS的实现原理</h1>
        <div class="post-meta">
            <time class="post-time" title="2022-04-09 14:35:20" datetime="2022-04-09T06:35:20.000Z"  itemprop="datePublished">2022-04-09</time>

            
	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/blog/categories/%E5%B9%B6%E5%8F%91/">并发</a></li></ul>



            
<span id="busuanzi_container_page_pv" title="文章总阅读量" style='display:none'>
    <i class="icon icon-eye icon-pr"></i><span id="busuanzi_value_page_pv"></span>
</span>


        </div>
        <div class="post-content" id="post-content" itemprop="postContent">
            <h1 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h1><p>&emsp;&emsp;这段时间在研究<code>Java</code>并发相关的内容，一段时间下来算是小有收获了。<code>ReentrantLock</code>是<code>Java</code>并发中的重要部分，所以也是我的首要研究对象，在学习它的过程中，我发现它是基于<strong>抽象队列同步器（AQS）</strong>实现的，所以我花了点时间学习了一下<code>AQS</code>的实现原理。这篇博客就来说一说<code>AQS</code>的作用，以及它是如何实现的。</p>
<br>

<h1 id="二、正文"><a href="#二、正文" class="headerlink" title="二、正文"></a>二、正文</h1><h2 id="2-1-什么是AQS"><a href="#2-1-什么是AQS" class="headerlink" title="2.1 什么是AQS"></a>2.1 什么是AQS</h2><p>&emsp;&emsp;<code>AQS</code>全称抽象队列同步器（AbstractQuenedSynchronizer），它是一个可以用来实现线程同步的基础框架。当然，它不是我们理解的<code>Spring</code>这种框架，它是一个类，类名就是<code>AbstractQuenedSynchronizer</code>，如果我们想要实现一个能够完成线程同步的锁或者类似的同步组件，就可以在使用<code>AQS</code>来实现，因为它封装了线程同步的方式，我们在自己的类中使用它，就可以很方便的实现一个我们自己的锁。</p>
<br>

<h2 id="2-2-如何使用AQS"><a href="#2-2-如何使用AQS" class="headerlink" title="2.2 如何使用AQS"></a>2.2 如何使用AQS</h2><p>&emsp;&emsp;<code>AQS</code>封装了很多方法，如获取独占锁，释放独占锁，获取共享锁，释放共享锁……我们可以通过在自己的实现的同步组件中调用<code>AQS</code>的这些方法来实现一个线程同步的功能。但是，根据<code>AQS</code>的名称也能够想到，我们不能直接创建<code>AQS</code>的对象，调用这些方法，因为<code>AQS</code>是一个抽象类，我们需要继承<code>AQS</code>，创建它的子类对象来使用它。在实际使用中，一般是在我们自己的类中，以内部类的方式继承<code>AQS</code>，然后在内部创建一个对象，在这个类内部使用，比如<code>ReentrantLock</code>中就是定义了一个抽象内部类<code>Sync</code>，继承<code>AQS</code>，然后定义了一个<code>NonfairSync</code>类，继承<code>Sync</code>，<code>NonfairSync</code>是一个<strong>非公平锁</strong>；同时又定义了一个<code>FairSync</code>类继承<code>Sync</code>，<code>FairSync</code>是一个<strong>公平锁</strong>。</p>
<blockquote>
<p><strong>公平锁</strong>：多个线程按照申请锁的顺序去获得锁，后申请锁的线程需要排队，等它之前的线程获得锁并释放后，它才能获得锁；</p>
<p><strong>非公平锁</strong>：线程获得锁的顺序于申请锁的顺序无关，申请锁的线程可以直接尝试获得锁，谁抢到就是谁的；</p>
</blockquote>
<p>&emsp;&emsp;我们继承了<code>AQS</code>，就可以直接调用它的方法了吗？当然不是。<code>Java</code>中提供的抽象组件，都是帮我们写好了通用的部分，但是一些具体的部分，还需要我们自己实现。举个比较简单的例子，<code>Java</code>中对自定义类型数组的排序，可以直接调用工具类的<code>sort</code>方法，<code>sort</code>方法已经实现了排序的算法，但是其中的比较过程是抽象的，需要我们自己实现，所以我们一般需要提供一个比较器（Comparator），或者让自定义类实现<code>Comparable</code>接口。这就是<strong>模板方法</strong>设计模式。</p>
<blockquote>
<p><strong>模板方法</strong>：在一个方法中实现了一个算法的流程，但是其中的一些步骤是抽象的，需要在子类中实现，或者具体使用时实现。模板方法可以提高算法的复用性，提供了算法的弹性，对于不同的需求，可以通用同一份代码。</p>
</blockquote>
<p>&emsp;&emsp;而<code>AQS</code>的实现就是封装了一系列的模板方法，包括获取锁、释放锁等，这些都是模板方法。这些方法中调用的一些方法并没有具体实现，需要使用者根据自己的需求，在子类中进行实现。下面我们就来看看<code>AQS</code>中的这些方法。</p>
<br>

<h2 id="2-3-AQS中的方法"><a href="#2-3-AQS中的方法" class="headerlink" title="2.3 AQS中的方法"></a>2.3 AQS中的方法</h2><p>&emsp;&emsp;<strong>AQS底层维护一个int类型的变量state来表示当前的同步状态，根据当前state的值，来判断当前释放处于锁定状态，或者是其他状态</strong>。而<code>state</code>的每一个值具体是什么含义，是由我们自己实现的。我们继承<code>AQS</code>时，根据自己的需求，实现一些方法，其中就是通过修改<code>state</code>的值来维持同步状态。而关于<code>state</code>，主要有以下三个方法：</p>
<ul>
<li>*<em>int getState() *</em>：获取当前同步状态<code>state</code>的值；</li>
<li>*<em>void setState(int newState) *</em>：设置当前同步状态<code>state</code>的值；</li>
<li>*<em>boolean compareAndSetState(int expect, int update) *</em>：使用<code>CAS</code>设置当前同步状态的值，方法能够保证设置同步状态时的原子性；参数<code>expect</code>为<code>state</code>的预期旧值，而<code>update</code>是需要修改的新值，若设置成功，方法返回<code>true</code>，否则<code>false</code>；</li>
</ul>
<blockquote>
<p>CAS是一种乐观锁，若不了解，可以看看这篇博客：<a href="https://www.cnblogs.com/tuyang1129/p/12585019.html" target="_blank" rel="noopener">并发——详细介绍CAS机制</a></p>
</blockquote>
<p>&emsp;&emsp;接下来我们再看一看在继承<code>AQS</code>时，我们可以重写的方法：</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="1.png" alt="" title="">
                </div>
                <div class="image-caption"></div>
            </figure>

<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="2.png" alt="" title="">
                </div>
                <div class="image-caption"></div>
            </figure>

<p>&emsp;&emsp;以上这些方法将会在<code>AQS</code>的模板方法中被调用，我们根据自己的需求，重写上述方法，控制同步状态<code>state</code>的值，即可控制线程同步的方式。下面再来看看<code>AQS</code>提供的模板方法：</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="3.png" alt="" title="">
                </div>
                <div class="image-caption"></div>
            </figure>

<p>&emsp;&emsp;<code>AQS</code>提供的模板方法主要分为三类：</p>
<ul>
<li>独占式地获取和释放锁；</li>
<li>共享式地获取和释放锁；</li>
<li>查询<code>AQS</code>的同步队列中正在等待的线程情况；</li>
</ul>
<p>&emsp;&emsp;下面我们就来具体说一说<code>AQS</code>是如何实现线程同步的。</p>
<br>

<h2 id="2-4-AQS如何实现线程同步"><a href="#2-4-AQS如何实现线程同步" class="headerlink" title="2.4 AQS如何实现线程同步"></a>2.4 AQS如何实现线程同步</h2><p>&emsp;&emsp;前面提过，<code>AQS</code>通过一个<code>int</code>类型的变量<code>state</code>来记录当前的同步状态，也可以理解为锁的状态，根据<code>state</code>的值的不同，可以判断当前锁是否已经被获取。就拿独占锁来说，若我们要实现的是一个独占锁，则锁被获取后，其他线程将无法获取锁，需要进入阻塞状态，等待锁被释放。而线程获取锁就是通过修改<code>state</code>的值来实现的，一个线程修改<code>state</code>成功，则表示它成功获得了锁；若失败，则表示已经有其他线程获得了锁，则它需要进入阻塞状态。下面我们就来聊一聊<code>AQS</code>如何实现维持多个线程等待的。</p>
<p>&emsp;&emsp;首先说明结论：<strong>AQS通过一个同步队列来维护当前获取锁失败，进入阻塞状态的线程</strong>。这个同步队列是一个双向链表，获取锁失败的线程会被封装成一个链表节点，加入链表的尾部排队，而<code>AQS</code>保存了链表的头节点的引用<code>head</code>以及链表的尾节点引用<code>tail</code>。这个同步队列如下所示：</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="4.png" alt="" title="">
                </div>
                <div class="image-caption"></div>
            </figure>

<p>&emsp;&emsp;在这个同步队列中，每个节点对应一个线程，每个节点都有一个<code>next</code>指针指向它的下一个节点，以及一个<code>prev</code>指针指向它的上一个节点。队列中的头节点<code>head</code>就是当前已经获取了锁，正在执行的线程对应的节点；而之后的这些节点，则对应着获取锁失败，正在排队的线程。</p>
<p>&emsp;&emsp;当一个线程获取锁失败，它会被封装成一个<code>Node</code>，加入同步队列的尾部排队，同时线程会进入阻塞状态。也就是说，在同步队列中，除了头节点对应的线程是运行状态，其余的线程都是等待睡眠状态。而当头节点对应的线程释放锁时，它会唤醒它的下一个节点（也就是上图中的第二个节点），被唤醒的节点对应的线程开始尝试获取锁，若获取成功，它就会将自己置为<code>head</code>，然后将原来的<code>head</code>移出队列。接下来我们就通过源码，具体分析一下<code>AQS</code>的实现过程。</p>
<br>

<h2 id="2-5-独占锁的获取与释放过程"><a href="#2-5-独占锁的获取与释放过程" class="headerlink" title="2.5 独占锁的获取与释放过程"></a>2.5 独占锁的获取与释放过程</h2><p>&emsp;<strong>（1）获取锁的实现</strong></p>
<p>&emsp;&emsp;<code>AQS</code>的锁功能齐全，它既可以用来实现独占锁，也可以用来实现共享锁。</p>
<blockquote>
<p><strong>独占锁</strong>：也叫排他锁，即锁只能由一个线程获取，若一个线程获取了锁，则其他想要获取锁的线程只能等待，直到锁被释放。比如说写锁，对于写操作，每次只能由一个线程进行，若多个线程同时进行写操作，将很可能出现线程安全问题；</p>
<p><strong>共享锁</strong>：锁可以由多个线程同时获取，锁被获取一次，则锁的计数器+1。比较典型的就是读锁，读操作并不会产生副作用，所以可以允许多个线程同时对数据进行读操作，而不会有线程安全问题，当然，前提是这个过程中没有线程在进行写操作；</p>
</blockquote>
<p>&emsp;&emsp;我们首先分析一下独占锁。在<code>AQS</code>中，通过方法<code>acquire</code>来获取独占锁，<code>acquire</code>方法的代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">        selfInterrupt();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;上面的方法执行流程如下：</p>
<ol>
<li><p>首先调用<code>tryAcquire</code>尝试获取一次锁，若返回<code>true</code>，表示获取成功，则<code>acquire</code>方法将直接返回；若返回<code>false</code>，则会继续向后执行<code>acquireQueued</code>方法；</p>
</li>
<li><p><code>tryAcquire</code>返回<code>false</code>后，将执行<code>acquireQueued</code>，但是这个方法传入的参数调用了<code>addWaiter</code>方法；</p>
</li>
<li><p><code>addWaiter</code>方法的作用是将当前线封装成同步队列的节点，然后加入到同步队列的尾部进行排队，并返回此节点；</p>
</li>
<li><p><code>addWaiter</code>方法执行完成后，将它的返回值作为参数，调用<code>acquireQueued</code>方法。<code>acquireQueued</code>方法的作用是让当前线程在同步队列中阻塞，然后在被其他线程唤醒时去获取锁；</p>
</li>
<li><p>若线程被唤醒并成功获取锁后，将从<code>acquireQueued</code>方法中退出，同时返回一个<code>boolean</code>值表示当前线程是否被中断，若被中断，则会执行下面的<code>selfInterrupt</code>方法，响应中断；</p>
<p>&emsp;&emsp;下面我们就来具体分析这个方法中调用的几个方法的执行流程。首先第一个<code>tryAcquire</code>方法：</p>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;可以看到，这个方法的实现仅仅只是抛出了一个异常。我们之前提过，<code>AQS</code>是基于模板方法设计模式实现的，在其中定义了许多模板方法，在模板方法中会调用一些没有实现的方法，这些方法需要使用者根据自己的需求实现。而<code>acquire</code>方法就是一个模板方法，其中调用的<code>tryAcquire</code>方法就是需要我们自己实现的方法。<code>tryAcquire</code>的作用就是尝试修改<code>state</code>值，也就是获取锁，若修改成功，则返回<code>true</code>，否则返回<code>false</code>。它的实现需要根据<code>AQS</code>的子类具体分析，比如<code>ReentrantLock</code>中的<code>Sync</code>，这里我就不详细叙述了，后面写一篇专门讲<code>ReentrantLock</code>的博客。下面来看看<code>addWaiter</code>的源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 将线程封装成一个节点，放入同步队列的尾部</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">addWaiter</span><span class="params">(Node mode)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 当前线程封装成同步队列的一个节点Node</span></span><br><span class="line">    Node node = <span class="keyword">new</span> Node(Thread.currentThread(), mode);</span><br><span class="line">    <span class="comment">// 这个节点需要插入到原尾节点的后面，所以我们在这里先记下原来的尾节点</span></span><br><span class="line">    Node pred = tail;</span><br><span class="line">    <span class="comment">// 判断尾节点是否为空，若为空表示队列中还没有节点，则不执行以下步骤</span></span><br><span class="line">    <span class="keyword">if</span> (pred != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 记录新节点的前一个节点为原尾节点</span></span><br><span class="line">        node.prev = pred;</span><br><span class="line">        <span class="comment">// 将新节点设置为新尾节点，使用CAS操作保证了原子性</span></span><br><span class="line">        <span class="keyword">if</span> (compareAndSetTail(pred, node)) &#123;</span><br><span class="line">            <span class="comment">// 若设置成功，则让原来的尾节点的next指向新尾节点</span></span><br><span class="line">            pred.next = node;</span><br><span class="line">            <span class="keyword">return</span> node;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 若以上操作失败，则调用enq方法继续尝试(enq方法见下面)</span></span><br><span class="line">    enq(node);</span><br><span class="line">    <span class="keyword">return</span> node;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">enq</span><span class="params">(<span class="keyword">final</span> Node node)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 使用死循环不断尝试</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">// 记录原尾节点</span></span><br><span class="line">        Node t = tail;</span><br><span class="line">        <span class="comment">// 若原尾节点为空，则必须先初始化同步队列，初始化之后，下一次循环会将新节点加入队列</span></span><br><span class="line">        <span class="keyword">if</span> (t == <span class="keyword">null</span>) &#123; </span><br><span class="line">            <span class="comment">// 使用CAS设置创建一个默认的节点作为首届点</span></span><br><span class="line">            <span class="keyword">if</span> (compareAndSetHead(<span class="keyword">new</span> Node()))</span><br><span class="line">                <span class="comment">// 首尾指向同一个节点</span></span><br><span class="line">                tail = head;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 以下操作与addWaiter方法中的if语句块内一致</span></span><br><span class="line">            node.prev = t;</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetTail(t, node)) &#123;</span><br><span class="line">                t.next = node;</span><br><span class="line">                <span class="keyword">return</span> t;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;以上就是<code>addWaiter</code>方法的实现过程，我在代码中使用注释对每一步进行了详细的解析，它的执行过程大致可以总结为：<strong>将新线程封装成一个节点，加入到同步队列的尾部，若同步队列为空，则先在其中加入一个默认的节点，再进行加入；若加入失败，则使用死循环（也叫自旋）不断尝试，直到成功为止</strong>。这个过程中使用<code>CAS</code>保证了添加节点的原子性。下面看看<code>acquireQueued</code>方法的源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 让线程不间断地获取锁，若线程对应的节点不是头节点的下一个节点，则会进入等待状态</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> node the node</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">acquireQueued</span><span class="params">(<span class="keyword">final</span> Node node, <span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 记录失败标志</span></span><br><span class="line">    <span class="keyword">boolean</span> failed = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 记录中断标志，初始为true</span></span><br><span class="line">        <span class="keyword">boolean</span> interrupted = <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// 循环执行，因为线程在被唤醒后，可能再次获取锁失败，需要重写进入等待</span></span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="comment">// 获取当前线程节点的前一个节点</span></span><br><span class="line">            <span class="keyword">final</span> Node p = node.predecessor();</span><br><span class="line">            <span class="comment">// 若前一个节点是头节点，则tryAcquire尝试获取锁，若获取成功，则执行if中的代码</span></span><br><span class="line">            <span class="keyword">if</span> (p == head &amp;&amp; tryAcquire(arg)) &#123;</span><br><span class="line">                <span class="comment">// 将当前节点设置为头节点</span></span><br><span class="line">                setHead(node);</span><br><span class="line">                <span class="comment">// 将原来的头节点移出同步队列</span></span><br><span class="line">                p.next = <span class="keyword">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                <span class="comment">// 失败标志置为false</span></span><br><span class="line">                failed = <span class="keyword">false</span>;</span><br><span class="line">                <span class="comment">// 返回中断标志，acquire方法可以根据返回的中断标志，判断当前线程是否被中断</span></span><br><span class="line">                <span class="keyword">return</span> interrupted;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// shouldParkAfterFailedAcquire方法判断当前线程是否能够进入等待状态，</span></span><br><span class="line">            <span class="comment">// 若当前线程的节点不是头节点的下一个节点，则需要进入等待状态，</span></span><br><span class="line">            <span class="comment">// 在此方法内部，当前线程会找到它的前驱节点中，第一个还在正常等待或执行的节点，</span></span><br><span class="line">            <span class="comment">// 让其作为自己的直接前驱，然后在需要时将自己唤醒（因为其中有些线程可能被中断），</span></span><br><span class="line">            <span class="comment">// 若找到，则返回true，表示自己可以进入等待状态了；</span></span><br><span class="line">            <span class="comment">// 则继续调用parkAndCheckInterrupt方法，当前线程在这个方法中等待，</span></span><br><span class="line">            <span class="comment">// 直到被其他线程唤醒，或者被中断后返回，返回时将返回一个boolean值，</span></span><br><span class="line">            <span class="comment">// 表示这个线程是否被中断，若为true，则将执行下面一行代码，将中断标志置为true</span></span><br><span class="line">            <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                parkAndCheckInterrupt())</span><br><span class="line">                interrupted = <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 上面代码中只有一个return语句，且return的前一句就是failed = false;</span></span><br><span class="line">        <span class="comment">// 所以只有当异常发生时，failed才会保持true的状态运行到此处；</span></span><br><span class="line">        <span class="comment">// 异常可能是线程被中断，也可能是其他方法中的异常，</span></span><br><span class="line">        <span class="comment">// 比如我们自己实现的tryAcquire方法</span></span><br><span class="line">        <span class="comment">// 此时将取消线程获取锁的动作，将它从同步队列中移除</span></span><br><span class="line">        <span class="keyword">if</span> (failed)</span><br><span class="line">            cancelAcquire(node);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;以上就是<code>acquireQueued</code>方法的源码分析。这个方法的作用可以概括为：<strong>让线程在同步队列中阻塞，直到它成为头节点的下一个节点，被头节点对应的线程唤醒，然后开始获取锁，若获取成功才会从方法中返回</strong>。这个方法会返回一个<code>boolean</code>值，表示这个正在同步队列中的线程是否被中断。</p>
<p>&emsp;&emsp;到此，获取独占锁的实现就分析完毕了。需要注意的是，这些过程中使用的<code>compareAndSetXXX</code>这种形式的方法，都是基于<code>CAS</code>机制实现的，保证了这些操作的原子性。</p>
<br>

<p>&emsp;<strong>（2）释放锁的实现</strong></p>
<p>&emsp;&emsp;分析完获取独占锁的代码后，我们再来看看释放锁的实现。释放独占锁是通过<code>release</code>方法实现的：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">release</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 调用tryRelease尝试修改state释放锁，若成功，将返回true，否则false</span></span><br><span class="line">    <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">        <span class="comment">// 若修改state成功，则表示释放锁成功，需要将当前线程移出同步队列</span></span><br><span class="line">        <span class="comment">// 当前线程在同步队列中的节点就是head，所以此处记录head</span></span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="comment">// 若head不是null，且waitStatus不为0，表示它是一个装有线程的正常节点，</span></span><br><span class="line">        <span class="comment">// 在之前提到的addWaiter方法中，若同步队列为空，则会创建一个默认的节点放入head</span></span><br><span class="line">        <span class="comment">// 这个默认的节点不包含线程，它的waitStatus就是0，所以不能释放锁</span></span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">            <span class="comment">// 若head是一个正常的节点，则调用unparkSuccessor唤醒它的下一个节点所对应的线程</span></span><br><span class="line">            unparkSuccessor(h);</span><br><span class="line">        <span class="comment">// 释放成功</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 释放锁失败</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;以上就是同步队列中头节点对应的线程释放锁的过程。<code>release</code>也是一个模板方法，其中通过调用<code>tryRelease</code>尝试释放锁，而<code>tryRelease</code>也需要使用者自己实现。在之前也说过，头节点释放锁时，需要唤醒它的下一个节点对应的线程，让这个线程不再等待，去获取锁，而这个过程就是通过<code>unparkSuccessor</code>方法实现的。</p>
<br>

<h2 id="2-6-共享锁的获取与释放过程"><a href="#2-6-共享锁的获取与释放过程" class="headerlink" title="2.6 共享锁的获取与释放过程"></a>2.6 共享锁的获取与释放过程</h2><p>&emsp;&emsp;前面提到过，<code>AQS</code>不仅仅可以用来实现独占锁，还可以用来实现共享锁，下面我们就来看看<code>AQS</code>中，有关共享锁的模板方法的实现。首先是获取共享锁的实现，在<code>AQS</code>中，定义了<code>acquireShared</code>方法用来获取共享锁：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquireShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryAcquireShared(arg) &lt; <span class="number">0</span>)</span><br><span class="line">        doAcquireShared(arg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;可以看到，这个方法比较简短。首先调用<code>tryAcquireShared</code>方法尝试获取一次共享锁，即修改<code>state</code>的值，若返回值<code>&gt;=0</code>，则表示获取成功，线程不受影响，继续向下执行；若返回值小于<code>0</code>，表示获取共享锁失败，则线程需要进入到同步队列中等待，调用<code>doAcquireShared</code>方法。<code>acquireShared</code>方法也是<code>AQS</code>的一个模板方法，而其中的<code>tryAcquireShared</code>方法就是需要使用者自己实现的方法。下面我们来看看<code>doAcquireShared</code>方法的实现：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 不间断地获取共享锁，若线程对应的节点不是头节点的下一个节点，将进入等待状态</span></span><br><span class="line"><span class="comment"> * 实现与acquireQueued非常类似</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> arg the acquire argument</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doAcquireShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 往同步队列的尾部添加一个默认节点，Node.SHARED是一个Node常量，</span></span><br><span class="line">    <span class="comment">// 它的值就是一个不带任何参数的Node对象，也就是new Node();</span></span><br><span class="line">    <span class="keyword">final</span> Node node = addWaiter(Node.SHARED);</span><br><span class="line">    <span class="comment">// 失败标志，默认为true</span></span><br><span class="line">    <span class="keyword">boolean</span> failed = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 中断标志，用来判断线程在等待的过程中释放被中断</span></span><br><span class="line">        <span class="keyword">boolean</span> interrupted = <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// 死循环不断尝试获取共享锁</span></span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="comment">// 获取默认节点的前一个节点</span></span><br><span class="line">            <span class="keyword">final</span> Node p = node.predecessor();</span><br><span class="line">            <span class="comment">// 判断当前节点的前一个节点是否为head节点</span></span><br><span class="line">            <span class="keyword">if</span> (p == head) &#123;</span><br><span class="line">                <span class="comment">// 尝试获取共享锁</span></span><br><span class="line">                <span class="keyword">int</span> r = tryAcquireShared(arg);</span><br><span class="line">                <span class="comment">// 若r&gt;0，表示获取成功</span></span><br><span class="line">                <span class="keyword">if</span> (r &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="comment">// 当前线程获取锁成功后，调用setHeadAndPropagate方法将当前线程设置为head</span></span><br><span class="line">                    <span class="comment">// 同时，若共享锁还能被其他线程获取，则在这个方法中也会向后传递，唤醒后面的线程</span></span><br><span class="line">                    setHeadAndPropagate(node, r);</span><br><span class="line">                    <span class="comment">// 将原来的head的next置为null</span></span><br><span class="line">                    p.next = <span class="keyword">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                    <span class="comment">// 判断当前线程是否中断，若被中断，则调用selfInterrupt方法响应中断</span></span><br><span class="line">                    <span class="keyword">if</span> (interrupted)</span><br><span class="line">                        selfInterrupt();</span><br><span class="line">                    <span class="comment">// 失败标志置为false</span></span><br><span class="line">                    failed = <span class="keyword">false</span>;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 以下代码和获取独占锁的acquireQueued方法相同，即让当前线程进入等待状态</span></span><br><span class="line">            <span class="comment">// 具体解析可以看上面acquireQueued方法的解析</span></span><br><span class="line">            <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                parkAndCheckInterrupt())</span><br><span class="line">                interrupted = <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (failed)</span><br><span class="line">            cancelAcquire(node);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;<code>doAcquireShared</code>方法的实现和获取独占锁中的<code>acquireQueued</code>方法很类似，但是主要有一点不同，那就是<strong>线程在被唤醒后，若成功获取到了共享锁，还需要判断共享锁是否还能被其他线程获取，若可以，则继续向后唤醒它的下一个节点对应的线程</strong>。下面再看看释放共享锁的代码，释放共享锁时通过方法<code>releaseShared</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">releaseShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 尝试修改state的值释放锁</span></span><br><span class="line">    <span class="keyword">if</span> (tryReleaseShared(arg)) &#123;</span><br><span class="line">        <span class="comment">// 若成功，则调用以下方法唤醒后继节点中的线程</span></span><br><span class="line">        doReleaseShared();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;<code>releaseShared</code>也是一个模板方法，它通过调用使用者自己实现的<code>tryReleaseShared</code>方法尝试释放锁，修改<code>state</code>的值，若返回<code>true</code>，表示修改成功，则继续向下调用<code>doReleaseShared</code>唤醒<code>head</code>的下一个节点对应的线程，让它开始尝试获取锁；若修改<code>state</code>失败，则返回<code>false</code>。</p>
<br>

<h2 id="2-7-使用AQS实现一个锁"><a href="#2-7-使用AQS实现一个锁" class="headerlink" title="2.7 使用AQS实现一个锁"></a>2.7 使用AQS实现一个锁</h2><p>&emsp;&emsp;介绍完上面的内容，下面我们就来基于<code>AQS</code>实现一个自己的同步器，或者说锁。我们需要实现的锁要求如下：</p>
<blockquote>
<p>&emsp;&emsp;实现一个锁，它是一个共享锁，但是每次至多支持两个线程同时获取锁，若当前已经有两个线程获取了锁，则其他获取锁的线程需要等待。</p>
</blockquote>
<p>&emsp;&emsp;实现代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 抽象队列同步器（AQS）使用：</span></span><br><span class="line"><span class="comment"> *      实现一个同一时刻至多只支持两个线程同时执行的同步器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 让当前类继承Lock接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TwinLock</span> <span class="keyword">implements</span> <span class="title">Lock</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义锁允许的最大线程数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="keyword">int</span> DEFAULT_SYNC_COUNT = <span class="number">2</span>;</span><br><span class="line">    <span class="comment">// 创建一个锁对象，用以进行线程同步，Sync继承自AQS</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Sync sync = <span class="keyword">new</span> Sync(DEFAULT_SYNC_COUNT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以内部类的形式实现一个同步器类，也就是锁，这个锁继承自AQS</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Sync</span> <span class="keyword">extends</span> <span class="title">AbstractQueuedSynchronizer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 构造方法中指定锁支持的线程数量</span></span><br><span class="line">        Sync(<span class="keyword">int</span> count) &#123;</span><br><span class="line">            <span class="comment">// 若count小于0，则默认为2</span></span><br><span class="line">            <span class="keyword">if</span> (count &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                count = DEFAULT_SYNC_COUNT;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 设置初始同步状态</span></span><br><span class="line">            setState(count);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 重写tryAcquireShared方法，这个方法用来修改同步状态state，也就是获取锁</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">tryAcquireShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">            <span class="comment">// 循环尝试</span></span><br><span class="line">            <span class="keyword">for</span> (; ; ) &#123;</span><br><span class="line">                <span class="comment">// 获取当前的同步状态</span></span><br><span class="line">                <span class="keyword">int</span> nowState = getState();</span><br><span class="line">                <span class="comment">// 计算当前线程获取锁后，新的同步状态</span></span><br><span class="line">                <span class="comment">// 注意这里使用了减法，因为此时的state表示的是还能支持多少个线程</span></span><br><span class="line">                <span class="comment">// 而当前线程如果获得了锁，则state就要减小</span></span><br><span class="line">                <span class="keyword">int</span> newState = nowState - arg;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 如果newState小于0，表示当前已经没有剩余的资源了</span></span><br><span class="line">                <span class="comment">// 则当前线程不能获取锁，此时将直接返回小于0的newState；</span></span><br><span class="line">                <span class="comment">// 或者newState&gt;0，就会执行compareAndSetState方法修改state的值，</span></span><br><span class="line">                <span class="comment">// 若修改成功将，将返回大于0的newState；</span></span><br><span class="line">                <span class="comment">// 若修改失败，则表示有其他线程也在尝试修改state，此时循环一次后，再次尝试</span></span><br><span class="line">                <span class="keyword">if</span> (newState &lt; <span class="number">0</span> || compareAndSetState(nowState, newState)) &#123;</span><br><span class="line">                    <span class="keyword">return</span> newState;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 尝试释放同步状态</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">tryReleaseShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">for</span> (; ; ) &#123;</span><br><span class="line">                <span class="comment">// 获取当前同步状态</span></span><br><span class="line">                <span class="keyword">int</span> nowState = getState();</span><br><span class="line">                <span class="comment">// 计算释放后的新同步状态，这里使用加法，</span></span><br><span class="line">                <span class="comment">// 表示有线程释放锁后，当前锁可以支持的线程数量增加了</span></span><br><span class="line">                <span class="keyword">int</span> newState = nowState + arg;</span><br><span class="line">                <span class="comment">// 使用CAS修改同步状态，若成功则返回true，否则自旋</span></span><br><span class="line">                <span class="keyword">if</span> (compareAndSetState(nowState, newState)) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取锁的方法</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 这里调用的是AQS的模板方法acquireShared，</span></span><br><span class="line">        <span class="comment">// 在acquireShared中将调用我们重写的tryAcquireShared方法</span></span><br><span class="line">        <span class="comment">// 传入参数为1表示当前线程，当前线程获取锁后，state将-1</span></span><br><span class="line">        sync.acquireShared(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 解锁</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 这里调用的是AQS的模板方法releaseShared，</span></span><br><span class="line">        <span class="comment">// 在acquireShared中将调用我们重写的tryReleaseShared方法</span></span><br><span class="line">        <span class="comment">// 传入参数为1表示当前线程，当前线程释放锁后，state将+1</span></span><br><span class="line">        sync.releaseShared(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*******************其他需要实现的方法省略***************************/</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;以上就实现了一个支持两个线程同时允许的共享锁，下面我们通过一个测试代码来测试效果：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">	<span class="comment">// 创建一个我们自定义的锁对象</span></span><br><span class="line">    Lock lock = <span class="keyword">new</span> TwinLock();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 启动10个线程去尝试获取锁</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">        Thread t = <span class="keyword">new</span> Thread(()-&gt;&#123;</span><br><span class="line">            <span class="comment">// 循环执行</span></span><br><span class="line">            <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">                <span class="comment">// 获取锁</span></span><br><span class="line">                lock.lock();</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 休眠1秒</span></span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">                    <span class="comment">// 输出线程名称</span></span><br><span class="line">                    System.out.println(Thread.currentThread().getName());</span><br><span class="line">                    <span class="comment">// 再次休眠一秒</span></span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    <span class="comment">// 释放锁</span></span><br><span class="line">                    lock.unlock();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">		<span class="comment">// 将线程设置为守护线程，主线程结束后，收获线程自动结束</span></span><br><span class="line">        t.setDaemon(<span class="keyword">true</span>);</span><br><span class="line">        t.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 主线程每隔1秒输出一个分割行</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">        Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">        System.out.println(<span class="string">"********************************"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&emsp;&emsp;以上测试代码运行后，在每两个分割行之间，最多不会输出超过两个线程的名称，线程名称的输出将会以两个一队出现。我的输出结果如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">********************************</span><br><span class="line">Thread-1</span><br><span class="line">Thread-0</span><br><span class="line">********************************</span><br><span class="line">********************************</span><br><span class="line">Thread-2</span><br><span class="line">Thread-1</span><br><span class="line">********************************</span><br><span class="line">********************************</span><br><span class="line">Thread-2</span><br><span class="line">Thread-1</span><br><span class="line">********************************</span><br><span class="line">********************************</span><br><span class="line">Thread-2</span><br><span class="line">Thread-3</span><br><span class="line">********************************</span><br><span class="line">********************************</span><br><span class="line">Thread-3</span><br><span class="line">Thread-4</span><br><span class="line">********************************</span><br></pre></td></tr></table></figure>

<br>

<h2 id="2-8-AQS如何实现线程等待"><a href="#2-8-AQS如何实现线程等待" class="headerlink" title="2.8 AQS如何实现线程等待"></a>2.8 AQS如何实现线程等待</h2><p>&emsp;&emsp;在研究<code>AQS</code>的过程中，我一直有这个疑惑——<code>AQS</code>如何让线程阻塞，直到最后才知道有一个叫<code>LockSupport</code>的工具类。这个工具类定义了很多静态方法，当需要让一个阻塞，或者唤醒一个线程时，就可以调用这个类中的方法，它的底层实现是通过一个<code>sun.misc.Unsafe</code>类的对象，<code>unsafe</code>类的方法都是本地方法，由其他语言实现，这个类是给不支持地址操作的<code>Java</code>，提供的一个操作内存地址的后门。</p>
<p>&emsp;&emsp;<code>AQS</code>中通过以下两个方法来阻塞和唤醒线程：</p>
<ul>
<li><strong>LockSupport.park()</strong>：阻塞当前线程；</li>
<li><strong>LockSupport.unpark(Thread thread)</strong>：将参数中传入的线程唤醒；</li>
</ul>
<p>&emsp;&emsp;前面讲解<code>AQS</code>的代码中，用到了方法<code>unparkSuccessor</code>，它的主要作用就是唤醒当前节点的下一个节点对应的线程，我们可以看看它的部分实现：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">unparkSuccessor</span><span class="params">(Node node)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ...........省略其他代码............</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 以下代码即为唤醒当前节点的下一个节点对应的线程</span></span><br><span class="line">    Node s = node.next;</span><br><span class="line">    <span class="keyword">if</span> (s != <span class="keyword">null</span>)</span><br><span class="line">        LockSupport.unpark(s.thread);	<span class="comment">// 使用LockSupport</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<br>

<h1 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h1><p>&emsp;&emsp;其实<code>AQS</code>还支持一些其他的方法，比如说在获取锁时设置超时时间等，这些方法的实现与上面介绍的几种大同小异，限于篇幅，这里就不进行叙述了。以上内容对<code>AQS</code>的实现原理以及主要方法的实现做了一个比较细致的介绍，相信看完之后会对<code>AQS</code>有一个比较深入的理解，但是想要理解以上内容，需要具备并发的一些基础知识，比如说线程的状态，<code>CAS</code>机制等。最后希望这篇博客对需要的人有所帮助吧。</p>
<br>

<h1 id="四、参考"><a href="#四、参考" class="headerlink" title="四、参考"></a>四、参考</h1><ul>
<li>《Java并发编程的艺术》</li>
<li><a href="https://www.cnblogs.com/zyrblog/p/9866140.html" target="_blank" rel="noopener">https://www.cnblogs.com/zyrblog/p/9866140.html</a></li>
</ul>

        </div>

        <blockquote class="post-copyright">
    
    <div class="content">
        
<span class="post-time">
    最后更新时间：<time datetime="2020-04-09T14:17:08.784Z" itemprop="dateUpdated">2020-04-09 22:17:08</time>
</span><br>


        
        世界是个球，前方总有路！
        
    </div>
    
    <footer>
        <a href="http://tewuyiang.gitee.io/blog">
            <img src="/blog/img/avatar.jpg" alt="特务依昂">
            特务依昂
        </a>
    </footer>
</blockquote>

        


        <div class="post-footer">
            
	<ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/blog/tags/%E5%B9%B6%E5%8F%91/" rel="tag">并发</a></li></ul>


            
<div class="page-share-wrap">
    

<div class="page-share" id="pageShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/&title=《【置顶】并发——抽象队列同步器AQS的实现原理》 — 博客主页&pic=http://tewuyiang.gitee.io/blog/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/&title=《【置顶】并发——抽象队列同步器AQS的实现原理》 — 博客主页&source=一个未来程序员的博客~~~" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《【置顶】并发——抽象队列同步器AQS的实现原理》 — 博客主页&url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/&via=http://tewuyiang.gitee.io/blog" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>



    <a href="javascript:;" id="shareFab" class="page-share-fab waves-effect waves-circle">
        <i class="icon icon-share-alt icon-lg"></i>
    </a>
</div>



        </div>
    </div>

    
<nav class="post-nav flex-row flex-justify-between flex-row-reverse">
  

  
    <div class="waves-block waves-effect next">
      <a href="/blog/HashMap%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB%E2%80%94%E2%80%94%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3HashMap%E9%AB%98%E6%95%88%E7%9A%84%E5%8E%9F%E5%9B%A0/" id="post-next" class="post-nav-link">
        <div class="tips">Next <i class="icon icon-angle-right icon-lg icon-pl"></i></div>
        <h4 class="title">【置顶】HashMap源码解读——深入理解HashMap高效的原因</h4>
      </a>
    </div>
  
</nav>



    




















</article>



</div>

        <footer class="footer">
    <div class="top">
        
<p>
    <span id="busuanzi_container_site_uv" style='display:none'>
        站点总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style='display:none'>
        站点总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


        <p>
            
                <span><a href="/blog/atom.xml" target="_blank" class="rss" title="rss"><i class="icon icon-lg icon-rss"></i></a></span>
            
            <span>博客内容遵循 <a rel="license noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" target="_blank">知识共享 署名 - 非商业性 - 相同方式共享 4.0 国际协议</a></span>
        </p>
    </div>
    <div class="bottom">
        <p><span>特务依昂 &copy; 2015 - 2020</span>
            <span>
                
                Power by <a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="https://github.com/yscoder/hexo-theme-indigo" target="_blank">indigo</a>
            </span>
        </p>
    </div>
</footer>

    </main>
    <div class="mask" id="mask"></div>
<a href="javascript:;" id="gotop" class="waves-effect waves-circle waves-light"><span class="icon icon-lg icon-chevron-up"></span></a>



<div class="global-share" id="globalShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/&title=《【置顶】并发——抽象队列同步器AQS的实现原理》 — 博客主页&pic=http://tewuyiang.gitee.io/blog/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/&title=《【置顶】并发——抽象队列同步器AQS的实现原理》 — 博客主页&source=一个未来程序员的博客~~~" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《【置顶】并发——抽象队列同步器AQS的实现原理》 — 博客主页&url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/&via=http://tewuyiang.gitee.io/blog" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://tewuyiang.gitee.io/blog/%E5%B9%B6%E5%8F%91%E2%80%94%E2%80%94%E6%8A%BD%E8%B1%A1%E9%98%9F%E5%88%97%E5%90%8C%E6%AD%A5%E5%99%A8AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>


<div class="page-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <img src="" alt="微信分享二维码">
</div>




    <script src="//cdn.bootcss.com/node-waves/0.7.4/waves.min.js"></script>
<script>
var BLOG = { ROOT: '/blog/', SHARE: true, REWARD: false };


</script>

<script src="//unpkg.com/hexo-theme-material-indigo@latest/js/main.min.js"></script>


<div class="search-panel" id="search-panel">
    <ul class="search-result" id="search-result"></ul>
</div>
<template id="search-tpl">
<li class="item">
    <a href="{path}" class="waves-block waves-effect">
        <div class="title ellipsis" title="{title}">{title}</div>
        <div class="flex-row flex-middle">
            <div class="tags ellipsis">
                {tags}
            </div>
            <time class="flex-col time">{date}</time>
        </div>
    </a>
</li>
</template>

<script src="//unpkg.com/hexo-theme-material-indigo@latest/js/search.min.js" async></script>






<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>



<script>
(function() {
    var OriginTitile = document.title, titleTime;
    document.addEventListener('visibilitychange', function() {
        if (document.hidden) {
            document.title = '人呢，怎么不见了！';
            clearTimeout(titleTime);
        } else {
            document.title = '(つェ⊂)咦!欢迎回来!';
            titleTime = setTimeout(function() {
                document.title = OriginTitile;
            },2000);
        }
    });
})();
</script>



</body>
</html>
